package com.phoenix.jsef.framework;

import com.phoenix.jsef.common.CUtils;
import com.phoenix.jsef.common.ConvertUtils;
import com.phoenix.jsef.common.ReflectionUtils;
import com.phoenix.jsef.framework.annotations.EntityAttribute;
import com.phoenix.jsef.framework.annotations.EntityCollectionAttribute;
import com.phoenix.jsef.framework.annotations.EntityFieldAttribute;
import com.phoenix.jsef.framework.annotations.EntitySubClassAttribute;
import com.phoenix.jsef.jdbcutils.CommandType;
import com.phoenix.jsef.jdbcutils.SqlHelper;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 该类为 填充实体对象的 帮助类
 * 
 * @author 李启联
 *
 */
public class FillEntityHelper  {

	/**
	 * 填充实体对象 如果实体对象内有定义其他实体对象的集合或单个实体属性 则会递归从数据库中取出对应的数据填充到对应的属性
	 * 该方法用于从数据库中查询到数据后，把数据填充到实体对象
	 * 
	 * @param map
	 *            准备好的 已有的map对象 可以理解为DataRow
	 * @param type
	 *            欲填充的实体对象的类型
	 * @param prefix
	 *            列名的前缀
	 * @return 返回填充好的实体
	 * @throws Exception
	 */
	public static Entity fillEntity(Map<String, Object> map, Type type, String prefix) throws Exception {
		
		if(map == null || map.isEmpty())
			return null;
		
		if (!prefix.isEmpty() || !prefix.equals(""))
			prefix += "_";

		Entity entity = (Entity) Class.forName(type.getTypeName()).newInstance();

		String fieldName = "", columnName = "", // 默认列名和entity的fieldName的名称一致，如果没有用EntityFieldAttribute注解标注的话。
												// 例如：String
												// job_Order_Id;数据库中的列表为job_Order_Id，entity的属性名称也为job_Order_Id
				columnNameDB = "";// entity的fieldName上用EntityFieldAttribute注解标注的columnName数据库中字段映射。
									// @EntityFieldAttribute(columnName="job_Order_Id")
									// String jobOrderId;
		Field[] fields = Class.forName(type.getTypeName()).getDeclaredFields();

		for (Field field : fields) { // 遍历所有属性		
			
			if(ReflectionUtils.isStaticFinalField(field))//如果该字段使用了static final 修饰 【通常这么修饰的字段是用来定义常量的】
				continue;//	遍历下一个
			
			fieldName = field.getName(); // 获取属性的名字
			columnName = prefix + CUtils.camelToUnderline(fieldName);// 把属性名称的驼峰命名转换成数据库中下划线命名的列名

			EntityFieldAttribute filedAttribute = field.getAnnotation(EntityFieldAttribute.class);
            if(filedAttribute != null && !filedAttribute.exist())//如果该属性标记了在数据库中不存在
                continue;//	遍历下一个

			if (filedAttribute != null && filedAttribute.columnName().length() > 0)// 如果在实体中标记了其在数据库中对应的列名称则优先用此标记的名称
				columnName = filedAttribute.columnName().toLowerCase();

			field.setAccessible(true);
			// if(field.getType() != EntityCollection.class &&
			// field.getType().getSuperclass() != Entity.class){
			if (field.getType() != EntityCollection.class && !Entity.class.isAssignableFrom(field.getType())
					&& !Collection.class.isAssignableFrom(field.getType()) //!field.getType().isAssignableFrom(List.class)
                ) {// 之所以加这层判断是为了防止
			       // 此类型的属性命名时和数据库中表对象的字段有对应关系的冲突
				if ((map.containsKey(columnName) && map.get(columnName) == null) || !map.containsKey(columnName))
					continue;
			}

			if (field.getType() == EntityCollection.class) {// 如果属性是另外类型实体的集合
				field.set(entity, fillEntityCollection(entity, field, map));
			} else if (Collection.class.isAssignableFrom(field.getType())//field.getType().isAssignableFrom(List.class)
					&& Entity.class.isAssignableFrom(ReflectionUtils.getFieldClass(field))) { // 如果属性是另外类型实体的泛型集合
				field.set(entity, fillCollectionWithGenericParam(entity, field, map));
			} else if (field.getType().getSuperclass() == Entity.class) {// 如果属性是另外类型的单个实体
																		 // 考虑到这种形式的实体对象是由数据库表反向生成的（天生就具有一一对应的关系）
																		 // 这样就无需考虑多重继承问题
																		 // 如此以来也便于规范和统一
				// else if (Entity.class.isAssignableFrom(field.getType())) {//如果属性是另外类型的单个实体，(这样写的好处可以解决实体多重继承问题 上面注释的代码有局限性)
				EntitySubClassAttribute subAttribute = field.getAnnotation(EntitySubClassAttribute.class);
				if (subAttribute == null && subAttribute.parentKeyProperty() == null)
					continue;
				String parentKeyName = ReflectionUtils.getPropertyName(entity.getClass(),
						CUtils.underlineToCamel(subAttribute.parentKeyProperty()));
				Object parentKeyValue = (Object) ReflectionUtils.getFieldValue(entity, parentKeyName);

				// 如果成员实体在实体中定义的位置在该实体对应的参引属性之前，为保证该成员实体能填充，其参引值需要从 map 中读取
				if (null == parentKeyValue) {
					columnName = CUtils.camelToUnderline(parentKeyName);
					EntityFieldAttribute filedAtt = (EntityFieldAttribute) entity.getClass()
							.getDeclaredField(parentKeyName).getAnnotation(EntityFieldAttribute.class);
					if (filedAtt != null && filedAtt.columnName() != null)
						columnName = filedAtt.columnName().toLowerCase();

					parentKeyValue = map.get(columnName);
				}
				Map<String, Object> _map = searchEntityById(field.getType(), parentKeyValue);
				field.set(entity, fillEntity(_map, field.getType(), prefix));
			}
			/*else if(field.getType() == Boolean.class || field.getType() == boolean.class) {
				if (map.containsKey(columnName) && map.get(columnName) != null) {
					field.set(entity,Boolean.valueOf(map.get(columnName).toString()));
				}
			}*/
			else {// 如果属性类型是 'string','Date','int', 'long','Double'......
				/*
				 * columnNameDB = ""; if
				 * (field.isAnnotationPresent(EntityFieldAttribute.class)) {
				 * EntityFieldAttribute fieldAttribute = (EntityFieldAttribute)
				 * field .getAnnotation(EntityFieldAttribute.class);
				 * columnNameDB = fieldAttribute.columnName().toLowerCase(); }
				 * if (!columnNameDB.equals("")) {//
				 * 如果entity的属性有用@EntityFieldAttribute(columnName="XXX")标注 if
				 * (map.get(columnNameDB) != null) { field.set(entity,
				 * ConvertUtils.convert(map.get(columnNameDB),
				 * field.getType())); } } else { if (map.get(columnName) !=
				 * null) { field.set(entity,
				 * ConvertUtils.convert(map.get(columnName), field.getType()));
				 * } }
				 */
				if (map.containsKey(columnName) && map.get(columnName) != null) {
					//field.set(entity, ConvertUtils.trytoConvert(map.get(columnName), field.getType()));
					field.set(entity, ConvertUtils.convertObjectToTargetType(map.get(columnName), field.getType()));
				}
			}
		}

		return entity;
	}

	/**
	 * 通过实体对象的主键值返回该实体对象对应的Map对象
	 * 
	 * @param type
	 *            ：实体对象类型
	 * @param obj
	 *            ：实体对象identityProperty 的值
	 * @return Map
	 * @throws ClassNotFoundException
	 * @throws Exception
	 */
	public static Map<String, Object> searchEntityById(Type type, Object obj) throws Exception {
		if(obj == null)
			return null;
		Map<String, Object> map = null;
		if (Class.forName(type.getTypeName()).isAnnotationPresent(EntityAttribute.class)) {
			EntityAttribute attribute = (EntityAttribute) Class.forName(type.getTypeName())
					.getAnnotation(EntityAttribute.class);
			String tableName = attribute.selectTable();
			String id = // CUtils.camelToUnderline(attribute.identityProperty());
						// 此写法增强了此注解的灵活性 可以为数据库字段的名称 也可以为实体模型属性名称
					CUtils.camelToUnderline(ReflectionUtils.getPropertyName(Class.forName(type.getTypeName()),
							CUtils.underlineToCamel(attribute.identityProperty())));

			List list = SqlHelper.queryForList(CommandType.Text,
					"SELECT * FROM " + tableName + " WHERE " + id + " = ?", obj);
			if (!list.isEmpty()) {
				map = (Map<String, Object>) list.get(0);
			}
		}
		return map;
	}

	public static Object getMappingValue(Type type,String targetFieldName,String keyFieldName,Object keyValue) throws Exception{
	    Object result = null;

        if (Class.forName(type.getTypeName()).isAnnotationPresent(EntityAttribute.class)) {
            EntityAttribute attribute = (EntityAttribute) Class.forName(type.getTypeName())
                .getAnnotation(EntityAttribute.class);
            String tableName = attribute.selectTable();
            targetFieldName = CUtils.camelToUnderline(ReflectionUtils.getPropertyName(Class.forName(type.getTypeName()), CUtils.underlineToCamel(targetFieldName)));
            keyFieldName = CUtils.camelToUnderline(ReflectionUtils.getPropertyName(Class.forName(type.getTypeName()), CUtils.underlineToCamel(keyFieldName)));

            List list = SqlHelper.queryForList(CommandType.Text,
                "SELECT "+ targetFieldName +" FROM " + tableName + " WHERE " + keyFieldName + " = ?", keyValue);
            if (!list.isEmpty()) {
                result = CUtils.getValueFromMapByLikeKey(targetFieldName,(Map<String, Object>)list.get(0));
            }
        }
	    return result;
    }

	/**
	 * 填充实体集合。
	 * 
	 * @param entity
	 *            待填充的实体。
	 * @param field
	 *            待填充实体的属性
	 * @param map
	 *            准备好的 已有的map对象 可以理解为DataRow
	 * @return 生成的实体集合
	 * @throws ClassNotFoundException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 */
	protected static EntityCollection fillEntityCollection(Entity entity, Field field, Map<String, Object> map) {
       
		if (!field.isAnnotationPresent(EntityCollectionAttribute.class) || map == null) {
			return null;
		}
		try {
			String columnName = "";
			EntityCollectionAttribute attribute = (EntityCollectionAttribute) field
					.getAnnotation(EntityCollectionAttribute.class);

			Class elementType = attribute.elementType();
			String foreignKey = ReflectionUtils.getPropertyName(elementType,
					CUtils.underlineToCamel(attribute.foreignKeyProperty()));
			String referredKey = ReflectionUtils.getPropertyName(elementType,
					CUtils.underlineToCamel(attribute.referredKeyProperty()));
			Object referredValue = ReflectionUtils.getFieldValue(entity, referredKey); // entity.getClass().getField(referredKey).get(entity);

			// 如果成员实体集合在实体中定义的位置在该实体对应的参引属性之前，为保证该成员实体集合能填充，其参引值需要从 map 中读取
			if (referredValue == null) {
				columnName = referredKey;
				EntityFieldAttribute filedAttribute = (EntityFieldAttribute) entity.getClass()
						.getDeclaredField(referredKey).getAnnotation(EntityFieldAttribute.class);
				if (filedAttribute != null && filedAttribute.columnName() != null)
					columnName = filedAttribute.columnName();
				referredValue = map.get(columnName);
			}

			PhoenixRepository repository = new PhoenixRepository<>();
            AccessParameter parameter = new AccessParameter();
            parameter.getFilter().addEqual(CUtils.camelToUnderline(foreignKey), referredValue);
			//AccessComponent accessor = new AccessComponent();
			//accessor.getAccessParameter().getFilter().addEqual(CUtils.camelToUnderline(foreignKey), referredValue);
            //accessor.searchEntityCollection(elementType);
            return repository.searchEntityCollection(parameter,elementType);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 对应实体填充带泛型参数(参数类型是Entity的子类)Collection集合 属性 例：List<Entity> 或 Set<Entity>
	 * 
	 * @param entity
	 *            主实体
	 * @param field
	 *            主实体定义的属性
	 * @param map
	 *            主实体对应取得要填充的数据 类似DataRow
	 * @return 填充好的实体集合
	 */
	protected static Collection<?> fillCollectionWithGenericParam(Entity entity, Field field, Map<String, Object> map) {

		// 如果定义的属性的形式非 List<Entity> 或 Set<Entity> 即 ：带泛型参数(参数类型是Entity的子类)List集合
		if ((!Collection.class.isAssignableFrom(field.getType())
				&& !Entity.class.isAssignableFrom(ReflectionUtils.getFieldClass(field))) || map == null) {
			return null;
		}

		String columnName = "";
		Class elementType = null;
		String foreignKey = "";
		String referredKey = "";
		Object referredValue = null;

		try {
			if (field.isAnnotationPresent(EntityCollectionAttribute.class)) {
				EntityCollectionAttribute attribute = (EntityCollectionAttribute) field
						.getAnnotation(EntityCollectionAttribute.class);
				elementType = attribute.elementType();
				foreignKey = ReflectionUtils.getPropertyName(elementType,
						CUtils.underlineToCamel(attribute.foreignKeyProperty()));
				referredKey = ReflectionUtils.getPropertyName(entity.getClass(),
						CUtils.underlineToCamel(attribute.referredKeyProperty()));
			} else {// 如果未标注EntityCollectionAttribute注解
					// ，默认为foreignKey与referredKey相同 即为entity
					// 的identityProperty(主键)，主表的主键即为从表的外键
				elementType = ReflectionUtils.getFieldClass(field);// 得到List泛型参数类型
				EntityAttribute entityAttribute = entity.getClass().getAnnotation(EntityAttribute.class);
				String entity_identityProperty = ReflectionUtils.getPropertyName(entity.getClass(),
						CUtils.underlineToCamel(entityAttribute.identityProperty()));
				foreignKey = referredKey = entity_identityProperty;
			}

			referredValue = ReflectionUtils.getFieldValue(entity, referredKey);

			// 如果成员实体集合在实体中定义的位置在该实体对应的参引属性之前，为保证该成员实体集合能填充，其参引值需要从 map 中读取
			if (referredValue == null) {
				columnName = CUtils.camelToUnderline(referredKey);
				EntityFieldAttribute filedAttribute = (EntityFieldAttribute) entity.getClass()
						.getDeclaredField(referredKey).getAnnotation(EntityFieldAttribute.class);
				if (filedAttribute != null && filedAttribute.columnName() != null)
					columnName = filedAttribute.columnName();
				referredValue = map.get(columnName);
			}

			//AccessComponent accessor = new AccessComponent();
			//accessor.getAccessParameter().getFilter().addEqual(CUtils.camelToUnderline(foreignKey), referredValue);
			//return accessor.searchEntityCollection(elementType);
            PhoenixRepository repository = new PhoenixRepository<>();
            AccessParameter parameter = new AccessParameter();
            parameter.getFilter().addEqual(CUtils.camelToUnderline(foreignKey), referredValue);

            if(Set.class.isAssignableFrom(field.getType())){//如果属性定义是Set集合类型则需要把ArrayList转为HashSet
                return new HashSet<>(repository.searchEntityCollection(parameter,elementType));
            }
            else {
                return repository.searchEntityCollection(parameter, elementType);
            }

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}


}
